# -*- coding: utf-8 -*-

import os
import wx

import signal
import time
import cPickle

from librarydescription import Library
from utilities import FormatTime
from constants import _pklFile, _iconMapper


class ImportProcess(object):
    """
    A class which starts an external process to import a Python library.
    """
    
    def __init__(self, mainframe, initName, importName, version):

        # Store the input data passed in __init__
        self.MainFrame = mainframe

        self.initName = initName
        self.importName = importName
        self.version = version
        
        self.pythonVersion = mainframe.pythonVersion
        self.saveDir = os.path.split(wx.GetApp().GetDatabase())[0]
        
        # No process started, the main frame will take care of it        
        self.process = None
        # Let's keep track if we failed the compilation step or not
        self.failed = False
        

    def Start(self):
        """ Starts the scan process. """

        pythonFile = os.path.join(self.MainFrame.installDir, "modulehunter.py")
        cmd = '"%s" -u "%s" "%s" %s %s "%s"'%(self.pythonVersion, pythonFile, self.initName,
                                              self.importName, self.version, self.saveDir)
 
        # Monitor the elapsed time
        self.startTime = time.time()
            
        # Start the process, redirecting the output to catch stdio
        self.process = wx.Process(self.MainFrame)
        self.process.Redirect()
        # We want the process to be asynchronous
        self.pid = wx.Execute(cmd, wx.EXEC_ASYNC, self.process)


    def Kill(self):
        """ Kills the process. """

        # This doesn't work very well on Windows :-(        
        self.process.CloseOutput()
        self.process.Kill(self.pid, wx.SIGTERM, wx.KILL_CHILDREN)
        if wx.Platform in ["__WXGTK__", "__WXMAC__"]:
            try:
                os.kill(self.pid, signal.SIGTERM)
            except:
                pass

        self.Destroy()


    def Destroy(self):        
            
        self.process.Destroy()        
        self.MainFrame.process = None
        
        
    def HandleProcessMessages(self, processEnded=False):
        """
        Handles all the messages that come from input and error streams.
        
        **Parameters:**

        * processEnded: whether the process has just ended.
        """

        # Get the input stream
        istream = self.process.GetInputStream()
        self.ReadStream(istream, 0, processEnded)
        # Get the error stream
        estream = self.process.GetErrorStream()
        self.ReadStream(estream, 1, processEnded)

        if processEnded:
            if not self.failed:

                pickleFile = os.path.join(self.saveDir, _pklFile)

                if not os.path.isfile(pickleFile):
                    msg = "Something very bad happened during the scan process.\n\n"
                    msg += "Please send the log window output to the author."
                    self.MainFrame.RunError("Error", msg)
                    return
                
                fid = open(pickleFile, "rb")
                library_class = cPickle.load(fid)
                fid.close()

                self.MainFrame.AddPackage(library_class)
                # Process finished without errors, send a congratulation message
                msg = "Library '%s' successfully imported. Elapsed time: %s"%(self.importName, FormatTime(self.startTime))
                kind = "Message"
            else:

                msg = "Errors detected while scanning library '%s'. Please refer to the log window"%self.importName              
                self.MainFrame.Stop()
                kind = "Error"

            self.MainFrame.SendMessage(kind, msg)
                    

    def ReadStream(self, stream, isError, processEnded):
        """
        Reads the input/error streams (if any).

        
        **Parameters:**

        * stream: the stdout/stderr stream;
        * isError: whether the building process generated an error or not;
        * processEnded: whether the process has just ended.
        """

        if not stream.CanRead():
            return
        
        # There is some data, process it
        text = stream.read()
##        print text

        lines = text.split("\n")
        prevKind = None
        
        if isError:
            if "Traceback (" not in text:
                isError = False
                prevKind = "Warning"
            
        for line in lines:

            if not line.strip():
                continue
            
            message = line.split(":")
            kind, message = message[0].strip(), ":".join(message[1:])

            if kind not in _iconMapper:
                kind = "Message"
                message = line

            if isError:
                kind = "Error"

            if prevKind:
                kind = prevKind
                
            self.MainFrame.SendMessage(kind, message.rstrip("\n"))

        if isError:
            # Ah, is the error stream, something went wrong
            self.failed = True
        